Storing the State in a Remote Backend

Learn how to create a storage blob to hold and save the state of our cluster.

Creating an Azure storage blob#

Terraform maintains its internal information about the current state. That allows it to deduce what needs to be done and converge the actual into the desired state defined in *.tf files. Currently, that state is stored locally in the terraform.tfstate file. For now, there shouldn’t be anything exciting in it.

Let’s see the definition of terraform.tfstate.

Output of terraform.tfstate

The field that really matters is resources. It’s empty because we didn’t define any. We will, soon, but we’re not going to create anything related to our AKS cluster. At least not right away. What we need right now is a storage bucket, or, to use Azure terms, we need a storage account and a storage container.

Keeping Terraform’s state local is a bad idea. If it’s on a laptop, we won’t be able to allow others to modify the state of our resources. We’d need to send them the terraform.tfstate file through email, keep it on a network drive, or implement some other similar solution. That’s impractical.

We might be tempted to store it in Git, but that wouldn’t be secure. Instead, we’ll tell Terraform to keep the state in a storage container. Since we’re trying to define infrastructure as code, we won’t do that by executing a shell command nor will we go to the Azure console. We’ll tell Terraform to create the container. It will be the first resource Terraform manages.

Updating storage.tf#

We’re about to explore the azurerm_storage_account and azurerm_storage_container modules. They allow us to manage Azure storage. More information about this is available in the azurerm_storage_account and azurerm_storage_container documentation.

Let’s copy the file we’ve prepared, and take a look at the definition.

Output of storage.tf
  • We’re defining a storage account and storage container, and both are referenced as state. All resource entries are followed with a type (e.g., azurerm_storage_account) and a reference (e.g., state). We’ll see the usage of a reference later in one of the upcoming definitions.

  • Just as with the provider, the resource has several fields. Some are mandatory, while others are optional and often have predefined values.

  • We’re defining the name and the resource_group_name for the account. We also specified that it should be created inside a specific location.

  • Finally, we set the tier (account_tier) and the replication type (account_replication_type).

The container also has a name (almost all resources do). Since a container always lives inside an account, we have to specify the storage_account_name. And that’s where resource references come into play. Instead of hard-coding the name of the account, we’re using the name of the azurerm_storage_account referenced as state. Finally, we set the access type (container_access_type) to blob.

Azure storage blob

Just like before, the values of some of those fields are defined as variables. Others (those less likely to change) are hard-coded.

Feel free to explore those two resource types in more detail through the Terraform documentation later. For now, we’ll move forward and apply the new definition.

The output, limited to the relevant parts, is as follows.

Output of terraform apply

In this case, we can see the full list of all the resources that will be created. The “+” sign indicates that something will be created. Under different conditions, we could also observe those that would be modified (“∼”) or destroyed ("-").

Right now, Terraform has deduced that the actual state is missing the azurerm_storage_account and azurerm_storage_container resources. It also shows us which properties will be used to create those resources. Some have been defined by us, while others will be known after we apply that definition.

Finally, we’re asked whether we want to perform these actions. We should type “Yes” and press the “Enter” key.

Note: From now on out, we won’t explicitly explain that we need to confirm Terraform actions. Answer with “Yes” when actions need to be confirmed.

After we choose to proceed, the relevant parts of the output should be as follows.

Output of successful completion of terraform apply

We can see that two resources were added and nothing was changed or destroyed.

Since this is the first time we’ve created a resource with Terraform, it’s reasonable to be skeptical that it worked. We’ll confirm that the account was indeed created by listing all. Over time, we’ll gain confidence in Terraform and won’t have to validate that everything works correctly.

List the storage accounts

We can see from the output that the storage account “devopscatalog” exists. There might be others, but they aren’t relevant for our exercises.

Next, let’s see whether the storage container was created inside the account.

Viewing the storage container inside account

The output is as follows.

List of containers in storage account

The storage container was indeed created. Hurray!

Let’s imagine that someone else has executed terraform apply and that we aren’t sure what the state of the resources is. In such a situation, we can consult Terraform by asking it to show us the state.

Asking Terraform to show the current state

The output, limited to the relevant parts, is as follows.

Output of terraform show

There’s not much to look at. For now, we have only two resources (azurerm_storage_account and (azurerm_storage_container). As we keep progressing, that output will increase and, more importantly, it will always reflect the state of the resources managed by Terraform.

The previous output is a human-readable format of the state currently stored in terraform.tfstate. Let’s look at the definition of the file.

Output of terraform.tfstate

If we ignore the fields that are currently empty, and the few that are for Terraform’s internal usage, we can see that the state stored in that file contains the same information as what we saw through terraform show. The only important difference is that one is in Terraform’s internal format (terraform.tfstate), while the other (terraform show) is meant to be readable by humans.

Even though it’s not the case right now, the state could easily contain some confidential information. It’s currently stored locally, and we’ve already decided to move it to Azure Storage Container. That way we’ll be able to share it, it will be stored in a more reliable location, and it will be more secure.

Devops
Devops
Azure storage
blob
Azure storage...
Terraform Apply
Terraform Apply
Push
Push
Pull
Pull
Stores
Stores
*.tfstate
*.tfstate
Viewer does not support full SVG 1.1
Overview of DevOps interacting with storage bucket

Moving the state to bucket#

To move the state to the storage container, we’ll create an azurerm backend. We’ve already prepared a file just for that.

Viewing backend.tf#

The output is as follows.

Viewing the back-end definition file

There’s nothing special in that definition. We’re setting the resource group (resource_group_name), the storage account name (storage_account_name), the container name (container_name), and the key, which represents the blob where Terraform state will be stored.

Let’s apply the definitions and see what we’ll get.

The output, limited to the relevant parts, is as follows.

Output of terraform apply

Since we’re changing the location where Terraform should store the state, we have to initialize the project again. The last time we did that, it was because a plugin (azurerm) was missing. This time it’s because the init process will copy the state from the local file to the newly created bucket.

The output, limited to the relevant parts, is as follows.

Output of terraform init

Confirm copying the state by typing “Yes” and pressing the “Enter” key.

The process continues. It copies the state to the remote storage, which, from now on, will be used instead of the local file. Now we should be able to apply the definitions.

The output is as follows.

Output of terraform apply

As we can see, there was no need to apply the definitions. The latest addition does not define any new resources. We only added the location for the Terraform state. That change is internal, and it was applied through the init process.

Try it yourself#

You can try all of the commands used in this lesson in the code playground below. Press the “Run” button and wait for a few seconds for it to connect.

For ease of use, all of the commands above are combined in main.sh.

Please provide values for the following:
AZURE_BUCKET_NAME
Not Specified...
/
main.sh
provider.tf
storage.tf
variables.tf
terraform.tfstate
files

Terraform Providers

Creating the Control Plane